package com.sdy.sd;

import android.content.Context;
import android.content.Intent;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.ArrayList;

public class SdGame {
    static final String TAG = "SdGame";
    static final int YES = 0xFF;
    static final int NO = 0xFE;

    static final String[] DIFFI_TXT = { "テスト用", "初級", "中級", "高級", "専門級", "最高級" };
    static final int[] DIFFI_CNT = { 2, 20, 30, 40, 50, 64 };
    public int mGameLevel = 1; //default:test

    private int mDifficultLevel = DIFFI_CNT[mGameLevel]; //max level:81-17=64
    private int mRemainCnt = mDifficultLevel;

    private SdUnit[][] mUnits = new SdUnit[9][9];
    public int mGameSt = GMST_NA;
    public SdHint mHint = null;
    public FullscreenActivity mContext = null;

    public static final int GMST_NA                 = 0;
    public static final int GMST_INIT               = 1;
    public static final int GMST_GAMING             = 2;
    public static final int GMST_DONE_WIN           = 3; //win and in animation
    public static final int GMST_FLAG_WIN_ANI       = 0x100;
    public static final int GMST_DONE_FAILED        = 4;
    public static final int GMST_DONE_SEE_ANSWER    = 5;

    public SdGame(FullscreenActivity ctx) {
        mContext = ctx;
        mGameSt = GMST_NA;
        for(int i = 0; i < 9; ++i) {
            for(int j = 0; j < 9; ++j) {
                mUnits[i][j] = new SdUnit();
                mUnits[i][j].mValCandicate = -1;
                mUnits[i][j].mValUser = -1;
            }
        }
    }
    public int getUserSetCnt() {return mDifficultLevel - mRemainCnt;}

    public void newGame() {
        int[][] u = new int[9][9];
        u = SdInitDataMaker.getInitialData();
        for(int i = 0; i < 9; ++i) {
            for(int j = 0; j < 9; ++j) {
                mUnits[i][j].mValCandicate = u[i][j];
                mUnits[i][j].mValUser = u[i][j];
                mUnits[i][j].mProp = SdUnit.PROP_FIXED;
            }
        }
        subMaskInitialData();
        mHint.hintReset().invalidate();
        mRemainCnt = mDifficultLevel;
        mGameSt = GMST_INIT;
        mGameSt &= ~GMST_FLAG_WIN_ANI;
    }
    private void subMaskInitialData() {
        //mask parts
        int cnt = 9 * 9;
        int[] idxPoll = new int[cnt];
        for(int i = 0; i < cnt; ++i) {
            idxPoll[i] = i;
        }

        for(int i = 0; i < mDifficultLevel; ++i) {
            int rndIdx = (int)(Math.random() * cnt); //rnd[0, 1)  ->      [0, cnt)   [0, 80]

            int row = idxPoll[rndIdx] / 9;
            int col = idxPoll[rndIdx] % 9;
            //Log.e(TAG, "rndIdx:" + rndIdx + " ->[" + row + ", " + col +"]");
            mUnits[row][col].mValUser = -1;
            mUnits[row][col].mProp = SdUnit.PROP_FOR_USER;

            idxPoll[rndIdx] = idxPoll[cnt - 1];
            cnt--;
        }
    }

    public SdGame gameReset() {
        mHint.hintReset().invalidate();
        mRemainCnt = mDifficultLevel;
        mGameSt = GMST_INIT;

        for(int row = 0; row < 9; ++row) {
            for(int col = 0; col < 9; ++col) {
                if(!isForUser(row, col)) continue;
                mUnits[row][col].mValUser = -1;
                mUnits[row][col].mProp &= ~(SdUnit.PROP_USER_SET);
            }
        }
        Intent itt = new Intent("reqReset");
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(itt);
        return this;
    }

    public boolean canPopup(int row, int col) {
        return (mUnits[row][col].mProp & SdUnit.PROP_FOR_USER) != 0;
    }
    public boolean isSetDone(int row, int col) {
        assert((mUnits[row][col].mProp & SdUnit.PROP_FIXED) == 0);
        assert((mUnits[row][col].mProp & SdUnit.PROP_FOR_USER) != 0);
        return (mUnits[row][col].mProp & SdUnit.PROP_USER_SET) != 0;
    }

    private int isValInRow(int v, int row, int skipCol) {
        for(int j = 0; j < 9; ++j) {
            if(j == skipCol) continue;
            if(mUnits[row][j].mValUser == v) return j;
        }
        return NO;
    }
    private int isValInCol(int v, int col, int skipRow) {
        for(int i = 0; i < 9; ++i) {
            if(skipRow == i) continue;
            if(mUnits[i][col].mValUser == v) return i;
        }
        return NO;
    }
    private int isValInChute(int v, int chuteRow, int chuteCol, int skipRow, int skipCol) {
        for(int row = 3 * chuteRow; row < (3 * chuteRow + 3); ++row) {
            for(int col = 3 * chuteCol; col < (3 * chuteCol + 3); ++col) {
                if(skipRow == row && skipCol == col) continue;
                if(mUnits[row][col].mValUser == v) return ((col << 8) | row);
            }
        }
        return NO;
    }
    public ArrayList<Integer> getPopupListItems(int row, int col) {
        ArrayList<Integer> li = new ArrayList<Integer>();
        int noSkip = -1;
        String hint = "";
        for(int i = 1; i <= 9; ++i) {
            if(NO == isValInRow(i, row, noSkip)) {
                li.add(i);
                hint += Integer.toString(i) + "、";
            }
        }
        if(hint.length() > 0) hint = hint.substring(0, hint.length() - 1);
        else hint = "-";
        mHint.mRowHint = hint;

        hint = "";
        for(int j = 1; j <= 9; ++j) {
            if(NO == isValInCol(j, col, noSkip)) {
                if(!li.contains(j)) {
                    li.add(j);
                }
                hint += Integer.toString(j) + "、";
            }
        }
        if(hint.length() > 0) hint = hint.substring(0, hint.length() - 1);
        else hint = "-";
        mHint.mColHint = hint;

        hint = "";
        for(int k = 1; k <= 9; ++k) {
            if(NO == isValInChute(k, row / 3, col / 3, noSkip, noSkip)) {
                if(!li.contains(k)) {
                    li.add(k);
                }
                hint += Integer.toString(k) + "、";
            }
        }
        if(hint.length() > 0) hint = hint.substring(0, hint.length() - 1);
        else hint = "-";
        mHint.mChuteHint = hint;
        mHint.invalidate();
        return li;
    }

    public String getUnitText(int row, int col) {
        if(row < 0 || row > 8 || col < 0 || col > 8) return "E";
        if(-1 == mUnits[row][col].mValUser) return "";
        return Integer.toString(mUnits[row][col].mValUser);
    }
    public SdUnit getUnit(int row, int col) {
        if(row < 0 || row > 8 || col < 0 || col > 8) return null;
        return mUnits[row][col];
    }

    private void checkGameSt(Context ctx) {
        assert(mRemainCnt >= 0);
        if(mDifficultLevel == mRemainCnt) {
            if(GMST_GAMING == mGameSt) {
                Intent itt = new Intent("reqReset");
                LocalBroadcastManager.getInstance(ctx).sendBroadcast(itt);
            }
            mGameSt = GMST_INIT;
        } else if(0 == mRemainCnt) {
            //mGameSt = GMST_DONE;   //set at checkWin, should not in when X clicked
        } else {
            if(GMST_INIT == mGameSt) {
                Intent itt = new Intent("reqNewGame");
                LocalBroadcastManager.getInstance(ctx).sendBroadcast(itt);
            }
            if(GMST_GAMING != mGameSt) {
                mGameSt = GMST_GAMING;
                Intent itt = new Intent("notifyEnterGaming");
                LocalBroadcastManager.getInstance(ctx).sendBroadcast(itt);
            }
        }

    }
    public int onPopupItemSelected(int row, int col, String txt, Context ctx) {
        assert (txt.length() == 1);
        if(txt.equals(SdView.POP_CLEAR_CHAR)) {
            assert ((mUnits[row][col].mProp & SdUnit.PROP_USER_SET) != 0);
            mUnits[row][col].mProp &= ~(SdUnit.PROP_USER_SET);
            ++mRemainCnt;
            checkGameSt(ctx);
            mUnits[row][col].mValUser = -1;
            getPopupListItems(row, col); //refresh hint
            return mRemainCnt;
        }
        assert (Character.isDigit(txt.charAt(0)));
        int i = Integer.parseInt(txt);
        assert (i > 0 && i <= 9);
        if(-1 == mUnits[row][col].mValUser) {
            --mRemainCnt;
            checkGameSt(ctx);
        }
        mUnits[row][col].mValUser = i;
        mUnits[row][col].mProp |= SdUnit.PROP_USER_SET;
        getPopupListItems(row, col); //refresh hint
        return mRemainCnt;
    }

    public boolean isForUser(int row, int col) {
        return 0 != (mUnits[row][col].mProp & SdUnit.PROP_FOR_USER);
    }

    //0:win, RRCC02:row check failed, RRCC03: col check failed , RRCC04: chute check failed
    public int checkWin() {
        for(int row = 0; row < 9; ++row) {
            for(int col = 0; col < 9; ++col) {
                if((mUnits[row][col].mProp & SdUnit.PROP_FIXED) != 0) continue; // skip the part that user can not input
                int v = mUnits[row][col].mValUser;
                assert (-1 != v);
                if(NO != isValInRow(v, row, col)) {
                    mGameSt = GMST_DONE_FAILED;
                    return (row << 16) | (col << 8) | 2;
                }
                if(NO != isValInCol(v, col, row)) {
                    mGameSt = GMST_DONE_FAILED;
                    return (row << 16) | (col << 8) | 3;
                }
                if(NO != isValInChute(v, row / 3, col / 3, row, col)) {
                    mGameSt = GMST_DONE_FAILED;
                    return (row << 16) | (col << 8) | 4;
                }
            }
        }
        mGameSt = (GMST_DONE_WIN | GMST_FLAG_WIN_ANI);
        return 0;
    }

    public int showAnswer() {
        for(int row = 0; row < 9; ++row) {
            for(int col = 0; col < 9; ++col) {
                if((mUnits[row][col].mProp & SdUnit.PROP_FIXED) != 0) continue; // skip the part that user can not input
                mUnits[row][col].mValUser = mUnits[row][col].mValCandicate;
                mUnits[row][col].mProp = SdUnit.PROP_SEE_ANSWER;
            }
        }
        mGameSt = GMST_DONE_SEE_ANSWER;
        return 0;
    }

    public void onCheckNG(int row1, int col1, int row2, int col2) {
        mUnits[row1][col1].mProp |= SdUnit.PROP_CHECK_ERR;
        mUnits[row2][col2].mProp |= SdUnit.PROP_CHECK_ERR;
    }
    public int doCheck() {
        for(int row = 0; row < 9; ++row) {
            for(int col = 0; col < 9; ++col) {
                if((mUnits[row][col].mProp & SdUnit.PROP_FIXED) != 0) continue; // skip the part that user can not input
                int v = mUnits[row][col].mValUser;
                if(-1 == v) continue;

                int ck = isValInRow(v, row, col);
                if(NO != ck) {
                    onCheckNG(row, col, row, ck);
                    return -1;
                }

                ck = isValInCol(v, col, row);
                if(NO != ck) {
                    onCheckNG(row, col, ck, col);
                    return -1;
                }

                ck = isValInChute(v, row / 3, col / 3, row, col);
                if(NO != ck) {
                    onCheckNG(row, col, (ck & 0xFF), (ck >> 8));
                    return -1;
                }
            }
        }
        return 0;
    }
    public int resetCheckErr() {
        for(int row = 0; row < 9; ++row) {
            for(int col = 0; col < 9; ++col) {
                mUnits[row][col].mProp &= ~SdUnit.PROP_CHECK_ERR;
            }
        }
        return 0;
    }

    public void diffiLevelChanged() {
        mDifficultLevel = DIFFI_CNT[mGameLevel];
        mRemainCnt = mDifficultLevel;
    }
}

